
/**
 * AppleIIGo
 * Apple 3.5" Disk Drive class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include "Apple35DiskDrive.h"

Apple35DiskDrive::Apple35DiskDrive() :
FloppyDiskDrive(DISKDRIVETYPE_3_5) {
	isControl = false;
	isDiskInserted = false;
	isDiskSwitched = false;
	isDiskWriteProtected = false;
	isHeadStepInward = false;
	isHeadStepping = false;
	isMotorOn = false;
}

bool Apple35DiskDrive::mount(char * diskImagePath) {
	if (FloppyDiskDrive::mount(diskImagePath)) {
		isDiskInserted = true;
		isDiskSwitched = true;
		isWriteEnabled = diskImage->isWriteEnabled();
		isDiskWriteProtected = diskImage->isWriteProtected();
		
		return true;
	}
	
	return false;
}
		
void Apple35DiskDrive::unmount() {
	FloppyDiskDrive::unmount();
	isDiskInserted = false;
	isMotorOn = false;
}

void Apple35DiskDrive::setControl(bool value) {
	isControl = value;
}

void Apple35DiskDrive::setPhaseRegister(UINT32 phaseRegister) {
	bool isCommandEdge = (this->phaseRegister ^ phaseRegister) & PHASE3;
	bool isCommandRisingEdge = getBit(phaseRegister, PHASE3);
	UINT8 driveFunction = getDriveFunction();
	this->phaseRegister = phaseRegister;
	
	if (!isCommandEdge)
		return;

	if (!isDriveEnabled)
		return;
	
	if (isCommandRisingEdge)
		printf("Apple 3.5\" Disk Drive command:%02x\n",
			   driveFunction);
	
	switch (driveFunction) {
		case 0x0:	// Set step direction inward
			if (isCommandRisingEdge)
				isHeadStepInward = true;
			return;
		case 0x1:	// Set step direction outward
			if (isCommandRisingEdge)
				isHeadStepInward = false;
			return;
		case 0x3:	// Reset disk switched flag
			if (isCommandRisingEdge)
				isDiskSwitched = false;
			return;
		case 0x4:	// Step disk
			if (isCommandRisingEdge)
				isHeadStepping = true;
			return;
		case 0x8:	// Set motor on
			if (isCommandRisingEdge)
				isMotorOn = true;
			return;
		case 0x9:	// Set motor off
			if (isCommandRisingEdge)
				isMotorOn = false;
			return;
		case 0xd:	// Eject disk
			if (!isCommandRisingEdge)
				unmount();
			return;
	}
	
	printf("Apple 3.5\" Disk Drive Error: Unknown command %02x\n",
		   driveFunction);
}

void Apple35DiskDrive::onPhysicalPhaseUpdate() {
	if (!isHeadStepping)
		return;
	
	isHeadStepping = false;
	
	SINT32 trackIndex = diskImage->getTrackIndex();
	UINT32 trackNum = diskImage->getTrackNum();
	trackIndex += isHeadStepInward ? 1 : -1;
	
	if (trackIndex < 0) {
		trackIndex = 0;
		// to-do: stopper sound
	} else if (trackIndex >= trackNum) {
		trackIndex = trackNum - 1;
		// to-do: stopper sound
	}
	
	diskImage->setTrackIndex(trackIndex);
	
	updateDiskTrack();
}

/*void Apple35DiskDrive::setDriveEnabled(bool value) {
	FloppyDiskDrive::setDriveEnabled(value);
	
	if (!value)
		isMotorOn = false;
}*/

bool Apple35DiskDrive::senseInput() {
	if (!isDriveEnabled)
		return false;
	
	UINT8 driveFunction = getDriveFunction();
	printf("Apple 3.5\" Disk Drive read function:%02x\n",
		   driveFunction);
	
	switch (driveFunction) {
		case 0x0:	// !Is head step direction inward
			return !isHeadStepInward;
		case 0x1:	// Select head 0
			diskImage->setHeadIndex(0);
			updateDiskTrack();
			return false;
		case 0x2:	// !Is disk inserted
			return !isDiskInserted;
		case 0x3:	// Select head 1
			diskImage->setHeadIndex(1);
			updateDiskTrack();
			return false;
		case 0x4:	// !Is head stepping
			return !isHeadStepping;
		case 0x5:	// Reserved
			break;
		case 0x6:	// !Is disk write protected
			return !isDiskWriteProtected;
		case 0x7:	// Reserved
			break;
		case 0x8:	// !Is motor on
			return !isMotorOn;
		case 0x9:	// !Is one side
			return true;
		case 0xa:	// !Is track 0
			return !(diskImage->getTrackIndex() == 0);
		case 0xb:	// !Is disk ready (unconfirmed)
			return isHeadStepping;
		case 0xc:	// !Is disk switched (unconfirmed)
			return !isDiskSwitched;
		case 0xd:	// Reserved
			break;
		case 0xe:	// Tachometer (60 pulses per revolution)
			return (trackBitIndex * 2 * 60 / trackBitNum) & 0x1;
		case 0xf:	// !Is drive installed
			return false;
	}
	
	printf("Apple 3.5\" Disk Drive Error: Unknown function %02x\n",
		   driveFunction);
	return true;
}

UINT8 Apple35DiskDrive::getDriveFunction() {
	return (((phaseRegister & 0x4) >> 2) |
			(isControl << 1) |
			((phaseRegister & 0x3) << 2));
}
